/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: xmessage_parser.cpp
 *
 * Purpose: xmessage parser API define and implementation
 *
 * Developer:
 *   wen.gu , 2023-11-01
 *
 * TODO:
 *
 ***************************************************************************/

#include "xbus/com/internal/xmessage_parser.h"

#include "xbus/com/xmessage_impl.h"

namespace xbus::com::internal {

XMessageParser::XMessageParser(uint32_t max_pkg_size, ErrorHandler&& error_handler/* = nullptr*/)
    :max_pkg_size_(max_pkg_size),
    error_handler_(error_handler) {
    /** todo something */
}

//static core::XBufferFactory gFactory;

XMessageParser::~XMessageParser() {
    /** todo something */
}

XMessageSp XMessageParser::parse(uint8_t* & pdata, uint8_t* pend) {
    while (pdata < pend) {
        if ((pdata[XMSG_FIELD_OFFSET_STARTCODE] == XMSG_START_CODE_BYTE1) && (pdata[XMSG_FIELD_OFFSET_STARTCODE + 1] == XMSG_START_CODE_BYTE2)) {     
            uint32_t length = ByteRead4(pdata);
            printf("[%s.%d]====>: length:%u\n", __FUNCTION__, __LINE__, length);
            if (length >= sizeof(XMessageHeader)){         
                if (length <= static_cast<uint32_t>(pend - pdata)) {
                    if (XMSG_GET_PROTOCOL_VER(pdata[XMSG_FIELD_OFFSET_FLAGS]) != XMSG_PROTOCOL_VER) {
                        //return XErrC::Undefined; /** protocol mismatch */
                        pdata += length;    
                        reportError(XErrC::ProtocolVersionMismatch);
                        continue;
                    }

                    if (XMSG_FLAGS_TP_FLAG(pdata[XMSG_FIELD_OFFSET_FLAGS])) {   
                        uint32_t temp_total_pkg = ByteRead4(pdata + XMSG_FIELD_OFFSET_PKT_TOTAL);
                        uint32_t pkg_idx = ByteRead4(pdata + XMSG_FIELD_OFFSET_PKT_IDX);   
                        if (last_pkg_idx_ == XMSG_PKT_IDX_INVALID) {
                            if (pkg_idx != XMSG_PKT_IDX_START) { /** not the start of a valid message, so just skip current message */
                                pdata += length;
                                reportError(XErrC::InvalidMessageStart);
                                continue;
                            } else {    
                                total_size_ = temp_total_pkg * max_pkg_size_ + XMessage::GetHeaderLength(0);

                                if (msg_data_) {
                                    delete[] msg_data_;
                                }

                                msg_data_ = new uint8_t[total_size_];
                                if (!msg_data_) {
                                    pdata += length; //
                                    total_size_ = 0;
                                    reportError(XErrC::InsufficientReousces);
                                    continue;
                                }

                                appendData(pdata, XMessage::GetHeaderLength(0));
                                XMSG_FLAGS_TP_FLAG_DISABLE(msg_data_[XMSG_FIELD_OFFSET_FLAGS]);

                                pdata += length;
                                last_pkg_idx_ = pkg_idx;
                                total_pkg_ = temp_total_pkg;   
                                continue;
                            }
                        } else if (pkg_idx == (last_pkg_idx_ + 1)) { 
                                uint32_t hdr_size = XMessage::GetHeaderLength(pdata[XMSG_FIELD_OFFSET_FLAGS]);
                                bool ret = appendData(pdata + hdr_size, length - hdr_size);

                                if (!ret) {
                                    reportError(XErrC::Undefined);
                                    pdata += length;
                                    reset();
                                    continue;   
                                } 

                            if (pkg_idx == temp_total_pkg) {
                                if (total_pkg_ == temp_total_pkg) {
                                    return makeMessage();
                                } else {
                                    reset();
                                    reportError(XErrC::Undefined);
                                }
                            } else {
                                last_pkg_idx_ = pkg_idx;
                            }
                            pdata += length;
                            continue;                                    
                        } else { /** maybe have data lost, so skip current receved data */
                            reset();
                            pdata += length;
                            reportError(XErrC::PackageIndexMismatch);
                            continue;
                        }
                    } else {
                        auto msg_ptr = XMessageImpl::CopyMsg(pdata, length);
                        if (!msg_ptr) {
                            reset();
                            reportError(XErrC::InsufficientReousces);
                        }
                        pdata += length;
                        return msg_ptr;
                    }
                    pdata += length;
                } else { /** todo refine me?  for this case, is invalid package?  is invalid 'length' field?, maybe just skip 1 byte and start to parse again */
                    pdata = pend; 
                    reset();
                    reportError(XErrC::InvalidLength);
                    return nullptr;
                }
            }
        }
        pdata++;
    }

    reportError(XErrC::NotFound);
    return nullptr;
}

void XMessageParser::reportError(XErrC err) {
    if (error_handler_) {
        error_handler_(err);
    }
}

bool XMessageParser::appendData(const uint8_t* data, uint32_t size) {
    if ((recv_pos_ + size) <= total_size_) {
        memcpy(msg_data_ + recv_pos_, data, size);
        recv_pos_ += size;
        return true;
    }
    return false;
}

void XMessageParser::reset() {
    if (msg_data_) {
        delete[] msg_data_;
    }

    total_size_ = 0;
    recv_pos_ = 0;    
    total_pkg_ = 0;
    last_pkg_idx_ = XMSG_PKT_IDX_INVALID;
}

XMessageSp XMessageParser::makeMessage() {
    internal::ByteWrite4(msg_data_, recv_pos_);
    uint8_t* msg_buf = msg_data_;
    uint32_t msg_len = recv_pos_;
    msg_data_ = nullptr;
    reset();
    
    return XMessageImpl::BuildMsg(msg_buf, msg_len);
}

} //namespace xbus::com::internal